Go の main で複數の server を起動し終了する
1 つの main でやる
code:main.go
package main
import (
"context"
"errors"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"time"
"google.golang.org/grpc"
)
var shutdownTimeout time.Duration = 10 * time.Second
func main() {
ctx := context.Background()
ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
defer stop()
ctx, cancel := context.WithCancelCause(ctx)
// net/http の例
srv1 := &http.Server{Addr: ":0"}
go func(ctx context.Context) {
if err := srv1.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
cancel(err)
}
}(ctx)
// google.golang.org/grpc の例
srv2 := grpc.NewServer()
go func(ctx context.Context) {
listener, err := net.Listen("tcp", ":0")
if err != nil {
cancel(err)
return
}
if err := srv2.Serve(listener); err != nil {
cancel(err)
}
}(ctx)
// 終了処理
<-ctx.Done()
if err := context.Cause(ctx); err != nil && !errors.Is(err, context.Canceled) {
log.Fatalln(err.Error())
}
ctx, cancelT := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancelT()
var wg sync.WaitGroup
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
if err := srv1.Shutdown(ctx); err != nil {
log.Println(err.Error())
}
}(ctx)
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
stopped := make(chan struct{})
go func() {
srv2.GracefulStop()
close(stopped)
}()
select {
case <-stopped:
case <-ctx.Done():
}
}(ctx)
wg.Wait()
if err := context.Cause(ctx); err != nil && !errors.Is(err, context.Canceled) {
log.Fatalln(err.Error())
}
}
使ってゐる tool が github.com/pkg/errors 等古い
たぶん net/http 以外眼中に無い
graceful.Graceful() 函數は一般的な顏をしてゐるが
gRPC 用の作りや、batch サーバー等に使へる一般性のある作りも欲しい if err := start(); err != http.ErrServerClosed おやおや
欲しいもの
複數の server を起動した場合の graceful shutdown を扱へる
一部の server が起動に失敗する等した時に、殘りの server もちゃんと終了して欲しい
一般性のある interface で gracefult shutdown を扱へる
error の扱ひがナウい
Context で cancel できる
signal はもちろん trap できる
graceful shutdown に timeout を設定できる
process を分けて supervise する
code:cmd/superviser/main.go
func main() {
}
code:cmd/server1/main.go
func main() {
}
code:cmd/server2/main.go
func main() {
}